/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2021 Marcus Britanicus (https://gitlab.com/marcusbritanicus)
 * Copyright (c) 2021 Abrar (https://gitlab.com/s96Abrar)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 **/

#pragma once

#include <QMap>
#include <QRect>
#include <QObject>
#include <QString>
#include <QMimeData>
#include <wayland-client-protocol.h>

struct wl_buffer;
struct wl_output;

struct zwlr_data_control_manager_v1;
struct zwlr_data_control_device_v1;
struct zwlr_data_control_offer_v1;
struct zwlr_data_control_source_v1;
struct zwlr_data_control_device_v1_listener;
struct zwlr_data_control_offer_v1_listener;
struct zwlr_data_control_source_v1_listener;

namespace WQt {
    class DataControlManager;
    class DataControlDevice;
    class DataControlSource;
    class DataControlOffer;

    class MimeData;
}

/** Implementational details */
class PipeReader;
class PipeWriter;

class WQt::MimeData {
    public:
        MimeData();
        MimeData( const WQt::MimeData& );

        /**
         * Obtain the list of all stored formats.
         * The returned list will be in ascedning order.
         */
        QStringList formats();

        /**
         * Retrieve data corresponding to the given format.
         * If the format does not exist, an empty QByteArray is returned.
         */
        QByteArray data( QString );

        /**
         * Store the format, and the corresponding data
         * Setting an empty data to a format removes it.
         */
        void setData( QString format, QByteArray data );

        /**
         * Clear all the formats stored
         */
        void clear();

        /**
         * Convert to QMimeData*
         */
        QMimeData * toQMimeData();

        /**
         * The assignment operator
         */
        WQt::MimeData& operator=( const WQt::MimeData& );

        /**
         * The equality operator
         */
        bool operator==( const WQt::MimeData& );

    private:
        QMap<QString, QByteArray> mimeDataMap;
};

class WQt::DataControlManager : public QObject {
    Q_OBJECT;

    public:
        DataControlManager( zwlr_data_control_manager_v1 *dataMgr );
        ~DataControlManager();

        DataControlSource *createDataSource();
        DataControlDevice *getDataDevice( wl_seat * );

        /** Return the zwlr_data_control_manager_v1 internal pointer  */
        zwlr_data_control_manager_v1 *get();

    private:
        zwlr_data_control_manager_v1 *mObj;
};

class WQt::DataControlDevice : public QObject {
    Q_OBJECT;

    public:
        DataControlDevice( zwlr_data_control_device_v1 *dataDev );
        ~DataControlDevice();

        /** Always call this after connecting the signals to slots */
        void setup();

        void setSelection( DataControlSource *src );
        void setPrimarySelection( DataControlSource *src );

        /** Return the zwlr_data_control_device_v1 internal pointer  */
        zwlr_data_control_device_v1 *get();

    private:
        static void handleDataOffer( void *, struct zwlr_data_control_device_v1 *, struct zwlr_data_control_offer_v1 * );
        static void handleSelection( void *, struct zwlr_data_control_device_v1 *, struct zwlr_data_control_offer_v1 * );
        static void handleFinished( void *, struct zwlr_data_control_device_v1 * );
        static void handlePrimarySelection( void *, struct zwlr_data_control_device_v1 *, struct zwlr_data_control_offer_v1 * );

        zwlr_data_control_device_v1 *mObj;

        static const zwlr_data_control_device_v1_listener mListener;

        WQt::DataControlOffer *currentOffer      = nullptr;
        WQt::DataControlSource *currentSelection = nullptr;
        WQt::DataControlSource *currentPrimary   = nullptr;

        bool mIsSetup  = false;
        bool isPrimary = false;
        WQt::DataControlOffer *pendingOffer = nullptr;

    Q_SIGNALS:
        void selectionOffered( DataControlOffer * );
        void primarySelectionOffered( DataControlOffer * );
};

class WQt::DataControlSource : public QObject {
    Q_OBJECT;

    public:
        DataControlSource( zwlr_data_control_source_v1 *srcDev );
        ~DataControlSource();

        /** Set the selection data internally to serve it to clients */
        void setSelectionData( WQt::MimeData );

        /** Offer a mimetype to the clients */
        void offer( QString mimeType );

        /** Return the zwlr_data_control_source_v1 internal pointer  */
        zwlr_data_control_source_v1 *get();

    private:
        static void handleSend( void *, struct zwlr_data_control_source_v1 *, const char *, int32_t );
        static void handleCanceled( void *, struct zwlr_data_control_source_v1 * );

        zwlr_data_control_source_v1 *mObj;

        static const zwlr_data_control_source_v1_listener mListener;

        WQt::DataControlOffer *currentOffer;

        WQt::MimeData mMimeData;
        PipeWriter *writer;

    Q_SIGNALS:
        void dataRequested( QString, int32_t );
        void canceled();
};

class WQt::DataControlOffer : public QObject {
    Q_OBJECT;

    public:
        DataControlOffer();
        ~DataControlOffer();

        bool update( zwlr_data_control_offer_v1 *offer );
        void invalidate();
        bool isValid();

        /** This is a potentialy blocking operation */
        QByteArray retrieveData( QString mimeType );
        QStringList offeredMimeTypes();

        /** Return the zwlr_data_control_offer_v1 internal pointer  */
        zwlr_data_control_offer_v1 *get();

    private:
        static void handleOffer( void *, struct zwlr_data_control_offer_v1 *, const char * );

        zwlr_data_control_offer_v1 *mObj;
        static const zwlr_data_control_offer_v1_listener mListener;

        QStringList mMimeTypes;
        PipeReader *reader;

    Q_SIGNALS:
        void mimeTypeOffered( QString );
        void invalidated();
};
